
%{
#include "parser_state.hpp"
%}

%defines
%pure-parser
%expect 1
%parse-param { parser_state & ps }
%lex-param { parser_state & ps }

%token TOK_character TOK_cppscript_include TOK_double TOK_false TOK_identifier TOK_integer
%token TOK_other_include TOK_reserved_keyword TOK_error
%token TOK_string TOK_throw TOK_true TOK_var TOK_void
%token TOK_mod_eq TOK_left_eq TOK_xor_eq TOK_mul_eq TOK_add_eq TOK_right_eq
%token TOK_or_eq TOK_div_eq TOK_and_eq TOK_sub_eq
%token TOK_and TOK_or TOK_eq TOK_neq TOK_leq TOK_geq TOK_left TOK_right
%token TOK_plusplus TOK_minusminus TOK_coloncolon TOK_script_main
%token TOK_default TOK_do TOK_continue TOK_if TOK_finally TOK_goto TOK_return
%token TOK_switch TOK_break TOK_for TOK_else TOK_while TOK_case TOK_try TOK_catch
%token TOK_foreach TOK_null TOK_namespace TOK_using TOK_enable_pickle

%%

source_file: 
	includes declarations { ps.parse_complete(); } ;

includes: 
	TOK_cppscript_include other_includes | 
	TOK_cppscript_include ;

other_includes: 
	TOK_other_include | 
	other_includes TOK_other_include ;
	
declarations: 
	declaration | 
	declarations declaration ;

declaration: 
	function_declaration |
	function_definition |
	namespace |
	using_declaration |
	pickle_declaration ;

namespace:
	TOK_namespace TOK_identifier { ps.push_namespace($2); } '{' declarations '}' { ps.pop_namespace(); } ;

using_declaration:
	TOK_using id_expression ';' |
	TOK_using TOK_namespace id_expression ';' ;

pickle_declaration:
	TOK_enable_pickle '(' id_expression ')' ';' ;

function_declaration: 
	function_header ';' { ps.declare_function($1); } ;

function_definition: 
	function_header { ps.define_function($1); } block { ps.define_function_body($3); } ;

function_header: 
	return_value function_name '(' arguments ')' { $$ = function_header($1, $2, $4); } |
 	return_value function_name '(' ')' { $$ = function_header($1, $2, array() ); } ;

function_name: 
	TOK_identifier |
	TOK_script_main ;

arguments: 
	argument { $$=array($1); } | 
	arguments ',' argument { $1.push_back($3); $$=$1; } ;

argument: 
	TOK_var { $$ = create_arg(null,null); } | 
	TOK_var TOK_identifier { $$ = create_arg($2, null); } | 
	TOK_var '=' constant_expression { $$ = create_arg(null, $3); } | 
	TOK_var TOK_identifier '=' constant_expression { $$ = create_arg($2, $4) } ;

return_value: 
	TOK_void { $$=false; } | 
	TOK_var { $$=true; } ;



// Statements

statement: 
	labelled_statement |
	selection_statement |
	iteration_statement |
	finally_statement |
	jump_statement |
	try_block |
	declaration_statement | 
	empty_statement | 
	expression_statement | 
	block { ps.block_statement($1); } |
	using_declaration ;

block: 
	'{' { ps.push_block(); } statements '}' { $$=ps.pop_block(); } | 
	'{' '}' { ps.push_block(); $$=ps.pop_block(); } ;

statements: 
	statement | 
	statements statement ;

declaration_statement: 
	TOK_var var_declarations ';' ;

empty_statement: 
	';' ;

var_declarations: 
	var_declaration | 
	var_declarations ',' var_declaration ;

var_declaration: 
	TOK_identifier { ps.declare_default_local_variable($1); } | 
	TOK_identifier '=' constant_expression { ps.declare_local_variable($1, $3); } ;

expression_statement: 
	expression ';' { ps.expression_statement($1); } ;

labelled_statement:
	TOK_identifier ':' statement |
	TOK_case constant_expression ':' statement |
	TOK_default ':' statement ;

selection_statement:
	TOK_if '(' condition ')' statement |
	TOK_if '(' condition ')' statement TOK_else statement |
	TOK_switch '(' condition ')' statement ;

condition:
	expression |
	TOK_var TOK_identifier '=' assignment_expression ;

iteration_statement:
	TOK_while '(' condition ')' statement |
	TOK_do statement TOK_while '(' expression ')' ';' |
	TOK_for '(' for_init_statement condition ';' expression ')' statement |
	TOK_for '(' for_init_statement ';' expression ')' statement |
	TOK_for '(' for_init_statement condition ';' ')' statement |
	TOK_for '(' for_init_statement ';' ')' statement |
	TOK_foreach '(' TOK_identifier ',' expression ')' statement ;
	
finally_statement:
	TOK_finally '(' expression ')' ';' ;

for_init_statement:
	expression_statement |
	declaration_statement ;

jump_statement:
	TOK_break ';' |
	TOK_continue ';' |
	TOK_return ';'|
	TOK_return expression ';'|
	TOK_goto TOK_identifier ';' ;

try_block:
	TOK_try block TOK_catch '(' TOK_var ')' block |
	TOK_try block TOK_catch '(' TOK_var TOK_identifier ')' block ;



// Expressions

primary_expression: 
	literal | 
	'(' expression ')' { $$==$2; } | 
	id_expression { $$ = ps.id_expression($1); } ;

id_expression:
	TOK_coloncolon nested_name |
	nested_name;

nested_name:
	TOK_identifier { $$ = array($1); } |
	nested_name TOK_coloncolon TOK_identifier { $1.push_back($3); $$ = $1; } ;

postfix_expression:
	primary_expression |
	postfix_expression '[' expression ']' |
	postfix_expression '(' ')' |
	postfix_expression '(' expression_list ')' |
	TOK_var '(' ')' { $$ = ps.literal_expression(null); } |
	TOK_var '(' expression ')' { $$=$3; } |
	postfix_expression '.' TOK_identifier '(' ')' |
	postfix_expression '.' TOK_identifier '(' expression_list ')' |
	postfix_expression TOK_plusplus |
	postfix_expression TOK_minusminus ;

expression_list:
	assignment_expression |
	expression_list ',' assignment_expression ;

unary_expression:
	postfix_expression |
	TOK_plusplus unary_expression |
	TOK_minusminus unary_expression |
	unary_operator unary_expression ;

unary_operator: 
	'*' | '&' | '+' | '-' | '!' | '~' ;

multiplicative_expression:
	unary_expression |
	multiplicative_expression '*' unary_expression |
	multiplicative_expression '/' unary_expression |
	multiplicative_expression '%' unary_expression ;

additive_expression:
	multiplicative_expression |
	additive_expression '+' multiplicative_expression { $$ = ps.binary_expression(add, $1, $3); } |
	additive_expression '-' multiplicative_expression;

shift_expression: 
	additive_expression |
	shift_expression TOK_left additive_expression |
	shift_expression TOK_right additive_expression ;
	
relational_expression:
	shift_expression |
	relational_expression '<' shift_expression |
	relational_expression '>' shift_expression |
	relational_expression TOK_leq shift_expression |
	relational_expression TOK_geq shift_expression ;

equality_expression: 
	relational_expression |
	equality_expression TOK_eq relational_expression |
	equality_expression TOK_neq relational_expression ;

and_expression: 
	equality_expression | 
	and_expression '&' equality_expression ;

exclusive_or_expression: 
	and_expression | 
	exclusive_or_expression '^' and_expression ;

inclusive_or_expression: 
	exclusive_or_expression | 
	inclusive_or_expression '|' exclusive_or_expression ;

logical_and_expression: 
	inclusive_or_expression | 
	logical_and_expression TOK_and inclusive_or_expression ;

logical_or_expression: 
	logical_and_expression | 
	logical_or_expression TOK_or logical_and_expression ;

assignment_expression: 
	conditional_expression |
	logical_or_expression assignment_operator assignment_expression |
	throw_expression ;
// !! Remove assigment expressions ??

assignment_operator: 
	'=' | TOK_mul_eq | TOK_div_eq | TOK_mod_eq | TOK_add_eq | 
	TOK_sub_eq | TOK_left_eq | TOK_right_eq | TOK_and_eq | TOK_xor_eq | TOK_or_eq;

throw_expression: 
	TOK_throw { $$ = ps.throw_expression(); } | 
	TOK_throw assignment_expression { $$ = ps.throw_expression($2); } ;

constant_expression: 
	conditional_expression ;

literal: 
	TOK_integer { $$ = ps.literal_expression($1); } | 
	TOK_character { $$ = ps.literal_expression($1); } | 
	TOK_double { $$ = ps.literal_expression($1); } | 
	TOK_string { $$ = ps.literal_expression($1); } | 
	TOK_true { $$ = ps.literal_expression(true); } | 
	TOK_false { $$ = ps.literal_expression(false); } | 
	TOK_null { $$ = ps.literal_expression(null); } ;

conditional_expression: 
	logical_or_expression |
	logical_or_expression '?' expression ':' assignment_expression ;

expression: 
	assignment_expression ;

%%

